﻿namespace Pathfinder.Maptools.Exporter
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Reflection;
    using Bridge.Base.Constructs;
    using Bridge.Base.Converter;
    using Bridge.Exporter.Maptools;
    using Bridge.UI.Utilities;
    using Pathfinder.Base.Constructs;
    using Pathfinder.Base.Utilities;

    public class PathfinderMaptoolsExporter : CharacterOutputConverter<PathfinderCharacter>
    {
        public override bool Output(PathfinderCharacter character, FileInfo fileInfo, List<ConverterProperty> properties)
        {
            ConverterProperty winRarLocationProperty = properties.FirstOrDefault(property => property.Key.Equals("WinRar Location", StringComparison.OrdinalIgnoreCase));
            ConverterProperty portraitImageLocationProperty = properties.FirstOrDefault(property => property.Key.Equals("Portrait Image", StringComparison.OrdinalIgnoreCase));
            ConverterProperty tokenImageLocationProperty = properties.FirstOrDefault(property => property.Key.Equals("Token Image", StringComparison.OrdinalIgnoreCase));
            ConverterProperty labelProperty = properties.FirstOrDefault(property => property.Key.Equals("Label", StringComparison.OrdinalIgnoreCase));
            ConverterProperty gmLabelProperty = properties.FirstOrDefault(property => property.Key.Equals("GM Label", StringComparison.OrdinalIgnoreCase));

            string winRarLocation = null;
            string portraitImageLocation = null;
            string tokenImageLocation = null;
            string label = string.Empty;
            string gmLabel = string.Empty;
            string tempPortraitDefault = null;
            string tempTokenDefault = null;
            bool returnValue = false;
            try
            {
                if (winRarLocationProperty != null && winRarLocationProperty.Value != null && winRarLocationProperty.Value.ToString().Length > 0 && File.Exists(winRarLocationProperty.Value.ToString()))
                {
                    winRarLocation = winRarLocationProperty.Value.ToString();
                }
                else
                {
                    winRarLocation = @"C:\Program Files\WinRar\rar.exe";
                }

                if (portraitImageLocationProperty != null && portraitImageLocationProperty.Value != null && portraitImageLocationProperty.Value.ToString().Length > 0 && File.Exists(portraitImageLocationProperty.Value.ToString()))
                {
                    portraitImageLocation = portraitImageLocationProperty.Value.ToString();
                }
                else
                {
                    tempPortraitDefault = Path.Combine(Path.GetTempPath(), Path.GetTempFileName() + "PortraitDefault.png");

                    Assembly.GetExecutingAssembly().GetManifestResourceInfo("Pathfinder.Maptools.Exporter.PortraitDefault.png");
                    byte[] buffer = new byte[32768];
                    using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("Pathfinder.Maptools.Exporter.PortraitDefault.png"))
                    using (FileStream outputFileStream = new FileStream(tempPortraitDefault, FileMode.Create))
                    {
                        while (stream.Position < stream.Length)
                        {
                            int readByte = stream.Read(buffer, 0, buffer.Length);
                            if (readByte == 0) break;
                            outputFileStream.Write(buffer, 0, readByte);
                        }
                    }
                    portraitImageLocation = tempPortraitDefault;
                }

                if (tokenImageLocationProperty != null && tokenImageLocationProperty.Value != null && tokenImageLocationProperty.Value.ToString().Length > 0 && File.Exists(tokenImageLocationProperty.Value.ToString()))
                {
                    tokenImageLocation = tokenImageLocationProperty.Value.ToString();
                }
                else
                {

                    tempTokenDefault = Path.Combine(Path.GetTempPath(), Path.GetTempFileName() + "TokenDefault.png");

                    byte[] buffer = new byte[32768];
                    using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("Pathfinder.Maptools.Exporter.TokenDefault.png"))
                    using (StreamReader streamReader = new StreamReader(stream))
                    using (FileStream outputFileStream = File.OpenWrite(tempTokenDefault))
                    {
                        while (stream.Position < stream.Length)
                        {
                            int readByte = stream.Read(buffer, 0, buffer.Length);
                            if (readByte == 0) break;
                            outputFileStream.Write(buffer, 0, readByte);
                        }
                    }
                    tokenImageLocation = tempTokenDefault;
                }

                if (labelProperty != null && labelProperty.Value != null && labelProperty.Value.ToString().Length > 0)
                {
                    label = labelProperty.Value.ToString();
                }
                if (gmLabelProperty != null && gmLabelProperty.Value != null && gmLabelProperty.Value.ToString().Length > 0)
                {
                    gmLabel = gmLabelProperty.Value.ToString();
                }

                string sightType = string.Empty;
                foreach (KeyValuePair<SightType, int> sight in character.Sight)
                {
                    switch (sight.Key)
                    {
                        case SightType.Blind: sightType = "Blind"; break;
                        case SightType.Darkvision: sightType = sight.Value < 120 ? "Darkvision" : "Darkvision120"; break;
                        case SightType.Lowlight: sightType = "Lowlight"; break;
                        case SightType.Normal: sightType = "Normal"; break;
                    }
                    if (!string.IsNullOrEmpty(sightType))
                    {
                        break;
                    }
                }

                List<MaptoolsProperty> maptoolsProperties = new List<MaptoolsProperty>();
                string subTypes = string.Empty;
                foreach (SubType subType in character.SubTypes)
                {
                    if (subTypes.Length > 0)
                    {
                        subTypes += ",";
                    }
                    subTypes += string.Format("'{0}'", subType.ToString());
                }
                Dictionary<string, object> raceJson = new Dictionary<string, object>() {
                    { "name", string.Empty },
                    { "family", string.Empty },
                    { "type", character.CreatureType },
                    { "subtype", "[" + subTypes + "]" },
                    { "notes", string.Empty }};
                maptoolsProperties.Add(new MaptoolsProperty("Race", CreateJSONObject(raceJson)));
                maptoolsProperties.Add(new MaptoolsProperty("SizeM", ((int)character.Size).ToString()));
                maptoolsProperties.Add(new MaptoolsProperty("Level", character.TotalHitDice.ToString()));
                maptoolsProperties.Add(new MaptoolsProperty("Levels", string.Format("TurnLevel=0 ; ECL={0} ;", character.CR)));
                maptoolsProperties.Add(new MaptoolsProperty("HP", character.MaximumHitPoints.ToString()));
                int hpMaxMod = character.MaximumHitPoints - (character.TotalHitDice * character.TotalStats[StatType.Constitution].GetBonus());
                maptoolsProperties.Add(new MaptoolsProperty("HPmax", string.Format("[R: max( Level, ( {0} + (ConB * Level) ) ) )]", hpMaxMod)));
                maptoolsProperties.Add(new MaptoolsProperty("BAB", character.BAB.ToString()));
                maptoolsProperties.Add(new MaptoolsProperty("Reach", character.Reach.ToString()));
                maptoolsProperties.Add(new MaptoolsProperty("InitMod", character.InitBonus.ToString()));
                string movement = character.Speed[SpeedType.Base].ToString();
                foreach (KeyValuePair<SpeedType, int> speed in character.Speed)
                {
                    if (speed.Key == SpeedType.Base) continue;
                    movement += string.Format(", {0}:{1}", speed.Key, speed.Value);
                }
                maptoolsProperties.Add(new MaptoolsProperty("Movement", movement));
                int flyManuverability = -1;
                switch (character.FlyManuverability)
                {
                    case FlyManuverability.NA: flyManuverability = 0; break;
                    case FlyManuverability.Clumsy: flyManuverability = 1; break;
                    case FlyManuverability.Poor: flyManuverability = 2; break;
                    case FlyManuverability.Average: flyManuverability = 3; break;
                    case FlyManuverability.Good: flyManuverability = 4; break;
                    case FlyManuverability.Perfect: flyManuverability = 5; break;
                }
                Dictionary<string, object> speedJson = new Dictionary<string, object>() {
                    { "base", character.Speed.ContainsKey(SpeedType.Base) ? character.Speed[SpeedType.Base] : 30 },
                    { "climb", character.Speed.ContainsKey(SpeedType.Climb) ? character.Speed[SpeedType.Climb] : 0 },
                    { "swim", character.Speed.ContainsKey(SpeedType.Swim) ? character.Speed[SpeedType.Swim] : 0 },
                    { "burrow", character.Speed.ContainsKey(SpeedType.Burrow) ? character.Speed[SpeedType.Burrow] : 0 },
                    { "fly", character.Speed.ContainsKey(SpeedType.Fly) ? character.Speed[SpeedType.Fly] : 0 },
                    { "flymaneuver", flyManuverability },
                    { "encumbered", 0 },
                    { "special", string.Empty }};
                maptoolsProperties.Add(new MaptoolsProperty("Speed", CreateJSONObject(speedJson)));

                int strengthBonus = character.TotalStats[StatType.Strength].GetBonus();
                int dexterityBonus = character.TotalStats[StatType.Dexterity].GetBonus();
                int baseStrengthBonus = character.BaseStats[StatType.Strength].GetBonus();
                int baseDexterityBonus = character.BaseStats[StatType.Dexterity].GetBonus();

                int cmbAdd = (character.HasAgileManeuvers ? dexterityBonus : strengthBonus) + character.BAB + (-1 * (int)character.Size);
                int cmdAdd = strengthBonus + dexterityBonus + character.BAB + (-1 * (int)character.Size);
                int baseCMBAdd = baseStrengthBonus + character.BAB + (-1 * (int)character.Size);
                int baseCMDAdd = baseStrengthBonus + baseDexterityBonus + character.BAB + (-1 * (int)character.Size);
                int cmb = cmbAdd + character.CMB;
                int cmd = 10 + cmdAdd + character.CMD;
                int baseCMB = baseCMBAdd + character.CMB;
                int baseCMD = baseCMDAdd + character.CMD;

                maptoolsProperties.Add(new MaptoolsProperty("AC",
                    string.Format("{0}/{1}/{2}/{3}/{4}", character.FullAC, character.TouchAC, character.FlatFootedAC, cmd, cmd - Math.Max(baseDexterityBonus, 0))));
                maptoolsProperties.Add(new MaptoolsProperty("SpecialATK", character.SpecialAttacks ?? string.Empty));
                maptoolsProperties.Add(new MaptoolsProperty("SpecialQual", character.SpecialQualities ?? string.Empty));
                string drs = string.Empty;
                foreach (KeyValuePair<DRType, int> dr in character.DR)
                {
                    if (drs.Length > 0) drs += ", ";
                    drs += string.Format("{0}/{1}", dr.Value, dr.Key.ToString().Replace('_', ' '));
                }
                string drValues = string.Empty;
                foreach (DRType drType in Enum.GetValues(typeof(DRType)))
                {
                    if (drValues.Length > 0) drValues += ",";
                    drValues += string.Format("\"{0}\":{1}", GetDisplayName(drType).Replace(" ", ""), character.DR.ContainsKey(drType) ? character.DR[drType] : 0);
                }
                drValues += ",\"Note\":\"\"";
                string erValues = string.Empty;
                foreach (ERType erType in Enum.GetValues(typeof(ERType)))
                {
                    if (erValues.Length > 0) erValues += ",";
                    erValues += string.Format("\"{0}\":{1}", GetDisplayName(erType).Replace(" ", ""), character.ER.ContainsKey(erType) ? character.ER[erType] : 0);
                }
                erValues += ",\"Note\":\"\"";
                string drer = "{\"Damage Reduction\":{" + drValues + "},\"Energy Resistance\":{" + erValues + "}}";
                maptoolsProperties.Add(new MaptoolsProperty("DRER", drer));
                Dictionary<string, object> bonusTypedItems = new Dictionary<string, object>();
                bool isRaging = false;
                string activeItems = string.Empty;
                foreach (KeyValuePair<ACType, int> ac in character.AC)
                {
                    if (ac.Key == ACType.Dex) continue;
                    if (ac.Key == ACType.Size) continue;
                    if (ac.Key == ACType.Rage)
                    {
                        isRaging = true;
                        continue;
                    }
                    int t = -1;
                    string modName = string.Empty;
                    switch (ac.Key)
                    {
                        case ACType.Armor:
                            t = 6;
                            modName = "Armor";
                            break;
                        case ACType.Deflection:
                            t = 5;
                            modName = "Deflection";
                            break;
                        case ACType.Dodge:
                            t = 6;
                            modName = "Dodge";
                            break;
                        case ACType.Insight:
                            t = 9;
                            modName = "ACMod";
                            break;
                        case ACType.Luck:
                            t = 10;
                            modName = "ACMod";
                            break;
                        case ACType.Natural:
                            t = 12;
                            modName = "Natural";
                            break;
                        case ACType.Profane:
                            t = 13;
                            modName = "ACMod";
                            break;
                        case ACType.Sacred:
                            t = 16;
                            modName = "ACMod";
                            break;
                        case ACType.Shield:
                            t = 17;
                            modName = "Shield";
                            break;
                        case ACType.Monk:
                        case ACType.Other:
                            t = 0;
                            modName = "ACMod";
                            break;
                    }
                    int v = ac.Value;
                    Dictionary<string, object> modInfoDictionary = new Dictionary<string, object>() { { "v", v }, { "t", t } };
                    string modInfo = CreateJSONObject(modInfoDictionary);
                    Dictionary<string, object> modNameInfoDictionary = new Dictionary<string, object>() { { modName, modInfo } };
                    string modNameInfo = CreateJSONObject(modNameInfoDictionary);
                    Dictionary<string, object> modsDictionary = new Dictionary<string, object>() {
                            { "mods", modNameInfo },
                            { "cat", "Bridge.Pathfinder.Maptools" },
                            { "state", "NA" },
                            { "macro", string.Empty },
                            { "encumbered", 0 },
                            { "w", 0 },
                            { "note", string.Empty },
                            { "tip", string.Empty } };
                    string mods = CreateJSONObject(modsDictionary);
                    bonusTypedItems.Add(ac.Key.GetUIDisplayAttribute() != null ? ac.Key.GetUIDisplayAttribute().DisplayName : ac.Key.ToString(), mods);
                    if (activeItems.Length > 0) activeItems += ",";
                    activeItems += string.Format("\"{0}\"", ac.Key.GetUIDisplayAttribute() != null ? ac.Key.GetUIDisplayAttribute().DisplayName : ac.Key.ToString());
                }
                string allMods = CreateJSONObject(bonusTypedItems);
                Dictionary<string, object> privateJSONDictionary = new Dictionary<string, object>() {
                    { "ActiveTempModSets", isRaging ? "[\"Rage\"]" : "[]" },
                    { "ActiveItems", "[" + activeItems + "]" }
                };
                maptoolsProperties.Add(new MaptoolsProperty("PrivateJSON", CreateJSONObject(privateJSONDictionary)));
                maptoolsProperties.Add(new MaptoolsProperty("BonusTypedItems", allMods));
                maptoolsProperties.Add(new MaptoolsProperty("Strength", character.TotalStats[StatType.Strength].ToString()));
                maptoolsProperties.Add(new MaptoolsProperty("Dexterity", character.TotalStats[StatType.Dexterity].ToString()));
                maptoolsProperties.Add(new MaptoolsProperty("Constitution", character.TotalStats[StatType.Constitution].ToString()));
                maptoolsProperties.Add(new MaptoolsProperty("Intelligence", character.TotalStats[StatType.Intelligence].ToString()));
                maptoolsProperties.Add(new MaptoolsProperty("Wisdom", character.TotalStats[StatType.Wisdom].ToString()));
                maptoolsProperties.Add(new MaptoolsProperty("Charisma", character.TotalStats[StatType.Charisma].ToString()));

                maptoolsProperties.Add(new MaptoolsProperty("Fort", character.Saves[SaveType.Fort].ToString()));
                maptoolsProperties.Add(new MaptoolsProperty("Reflex", character.Saves[SaveType.Ref].ToString()));
                maptoolsProperties.Add(new MaptoolsProperty("Will", character.Saves[SaveType.Will].ToString()));

                Dictionary<string, int> craftConversionNumbers = new Dictionary<string, int>() { { "alchemy", 170 }, { "armor", 171 }, { "baskets", 172 }, { "books", 173 }, { "bows", 174 }, { "calligraphy", 173 }, { "carpentry", 176 }, { "cloth", 188 }, { "clothing", 188 }, { "glass", 169 + 10 }, { "jewelry", 169 + 9 }, { "leather", 169 + 10 }, { "locks", 169 + 11 }, { "paintings", 169 + 12 }, { "pottery", 169 + 13 }, { "sculptures", 169 + 14 }, { "ships", 169 + 15 }, { "shoes", 169 + 8 }, { "stonemasonry", 169 + 16 }, { "traps", 169 + 17 }, { "weapons", 169 + 18 } };
                Dictionary<string, int> knowledgeConversionNumbers = new Dictionary<string, int>() { { "Arcana", 147 }, { "Dungeoneering", 116 + 3 }, { "Engineering", 116 + 2 }, { "Geography", 116 + 4 }, { "History", 116 + 5 }, { "Local", 116 + 6 }, { "Nature", 116 + 7 }, { "Nobility", 116 + 8 }, { "Planes", 116 + 10 }, { "Religion", 116 + 9 } };
                Dictionary<string, int> performConversionNumbers = new Dictionary<string, int>() { { "Act", 159 + 1 }, { "Comedy", 159 + 2 }, { "Dance", 159 + 3 }, { "Keyboard instruments", 159 + 4 }, { "Oratory", 159 + 5 }, { "Percussion instruments", 159 + 6 }, { "String instruments", 159 + 8 }, { "Wind instruments", 159 + 9 }, { "Sing", 159 + 7 } };
                Dictionary<string, int> professionConversionNumbers = new Dictionary<string, int>() { { "architect", 189 + 2 }, { "baker", 189 + 4 }, { "barrister", 189 + 22 }, { "brewer", 189 + 3 }, { "butcher", 189 + 4 }, { "clerk", 189 + 2 }, { "cook", 189 + 4 }, { "courtesan", 189 + 22 }, { "driver", 189 + 5 }, { "engineer", 189 + 2 }, { "farmer", 189 + 6 }, { "fisherman", 189 + 7 }, { "gambler", 189 + 2 }, { "gardener", 189 + 9 }, { "herbalist", 189 + 9 }, { "innkeeper", 189 + 12 }, { "librarian", 189 + 2 }, { "merchant", 189 + 15 }, { "midwife", 189 + 22 }, { "miller", 189 + 14 }, { "miner", 189 + 22 }, { "porter", 189 + 15 }, { "sailor", 189 + 17 }, { "scribe", 189 + 18 }, { "shepherd", 189 + 10 }, { "stable master", 189 + 20 }, { "soldier", 189 + 19 }, { "tanner", 189 + 21 }, { "trapper", 189 + 11 }, { "woodcutter", 189 + 23 } };

                string allSkills = string.Empty;
                foreach (Skill skill in character.Skills)
                {
                    int subcat = -1;

                    string altstat = string.Empty;
                    if (skill.BaseStatType != skill.AlternateStatType)
                    {
                        switch (skill.AlternateStatType)
                        {
                            case StatType.Charisma: altstat = "ChaB"; break;
                            case StatType.Constitution: altstat = "ConB"; break;
                            case StatType.Dexterity: altstat = "DexB"; break;
                            case StatType.Intelligence: altstat = "IntB"; break;
                            case StatType.Strength: altstat = "StrB"; break;
                            case StatType.Wisdom: altstat = "WisB"; break;
                        }
                    }
                    Dictionary<string, object> skillDictionary = new Dictionary<string, object>() {
                        { "name", skill.Name },
                        { "rank", skill.Ranks },
                        { "classskill", skill.IsClassSkill ? 1 : 0 },
                        { "racialmod", 0 },
                        { "feats", 0 },
                        { "equip", 0 },
                        { "synergy", 0 },
                        { "misc", skill.MiscBonus },
                        { "notes", string.IsNullOrEmpty(skill.Subtype) ? string.Empty : string.Format("Converted Subtype: {0}", skill.Subtype) },
                        { "altstat", altstat}};
                    if (!string.IsNullOrEmpty(skill.Subtype))
                    {
                        switch (skill.Name)
                        {
                            case "Craft":
                                subcat = craftConversionNumbers.Any(skillEntry => skillEntry.Key.Equals(skill.Subtype, StringComparison.OrdinalIgnoreCase)) ? craftConversionNumbers.First(skillEntry => skillEntry.Key.Equals(skill.Subtype, StringComparison.OrdinalIgnoreCase)).Value : -1;
                                break;
                            case "Knowledge":
                                subcat = knowledgeConversionNumbers.Any(skillEntry => skillEntry.Key.Equals(skill.Subtype, StringComparison.OrdinalIgnoreCase)) ? knowledgeConversionNumbers.First(skillEntry => skillEntry.Key.Equals(skill.Subtype, StringComparison.OrdinalIgnoreCase)).Value : -1;
                                break;
                            case "Perform":
                                subcat = performConversionNumbers.Any(skillEntry => skillEntry.Key.Equals(skill.Subtype, StringComparison.OrdinalIgnoreCase)) ? performConversionNumbers.First(skillEntry => skillEntry.Key.Equals(skill.Subtype, StringComparison.OrdinalIgnoreCase)).Value : -1;
                                break;
                            case "Profession":
                                subcat = professionConversionNumbers.Any(skillEntry => skillEntry.Key.Equals(skill.Subtype, StringComparison.OrdinalIgnoreCase)) ? professionConversionNumbers.First(skillEntry => skillEntry.Key.Equals(skill.Subtype, StringComparison.OrdinalIgnoreCase)).Value : -1;
                                break;
                        }
                    }
                    if (subcat > -1)
                    {
                        skillDictionary.Add("subcat", subcat);
                    }
                    else
                    {
                        skillDictionary.Add("subcat", string.Empty);
                    }

                    if (allSkills.Length > 0) allSkills += ",";
                    allSkills += CreateJSONObject(skillDictionary);
                }
                Dictionary<string, object> craftSkillDictionary = new Dictionary<string, object>() {
                        { "name", "Craft" },
                        { "rank", 0 },
                        { "classskill", 0 },
                        { "racialmod", 0 },
                        { "feats", 0 },
                        { "equip", 0 },
                        { "misc", 0 },
                        { "notes", string.Empty },
                        { "subcat", 99 },
                        { "altstat", string.Empty}};
                Dictionary<string, object> performSkillDictionary = new Dictionary<string, object>() {
                        { "name", "Perform" },
                        { "rank", 0 },
                        { "classskill", 0 },
                        { "racialmod", 0 },
                        { "feats", 0 },
                        { "equip", 0 },
                        { "misc", 0 },
                        { "notes", string.Empty },
                        { "subcat", 99 },
                        { "altstat", string.Empty}};
                Dictionary<string, object> knowledgeSkillDictionary = new Dictionary<string, object>() {
                        { "name", "Craft" },
                        { "rank", 0 },
                        { "classskill", 0 },
                        { "racialmod", 0 },
                        { "feats", 0 },
                        { "equip", 0 },
                        { "misc", 0 },
                        { "notes", string.Empty },
                        { "subcat", 99 },
                        { "altstat", string.Empty}};
                Dictionary<string, object> professionSkillDictionary = new Dictionary<string, object>() {
                        { "name", "Craft" },
                        { "rank", 0 },
                        { "classskill", 0 },
                        { "racialmod", 0 },
                        { "feats", 0 },
                        { "equip", 0 },
                        { "misc", 0 },
                        { "notes", string.Empty },
                        { "subcat", 99 },
                        { "altstat", string.Empty}};
                allSkills += "," + CreateJSONObject(craftSkillDictionary);
                allSkills += "," + CreateJSONObject(performSkillDictionary);
                allSkills += "," + CreateJSONObject(knowledgeSkillDictionary);
                allSkills += "," + CreateJSONObject(professionSkillDictionary);

                maptoolsProperties.Add(new MaptoolsProperty("SkillsJ", "[" + allSkills + "]"));
                Dictionary<string, string> featDictionary = new Dictionary<string, string>() {
                    { "ImprovedInitiative", character.Feats.Any(feat => feat.Name.Equals("Improved Initiative", StringComparison.OrdinalIgnoreCase)) ? "1" : "0" },
                    { "AgileManeuvers", character.Feats.Any(feat => feat.Name.Equals("Agile Maneuvers", StringComparison.OrdinalIgnoreCase)) ? "1" : "0" },
                    { "ImprovedBullRush", character.Feats.Any(feat => feat.Name.Equals("Improved Bull Rush", StringComparison.OrdinalIgnoreCase)) ? "1" : "0" },
                    { "ImprovedDisarm", character.Feats.Any(feat => feat.Name.Equals("Improved Disarm", StringComparison.OrdinalIgnoreCase)) ? "1" : "0" },
                    { "ImprovedGrapple", character.Feats.Any(feat => feat.Name.Equals("Improved Grapple", StringComparison.OrdinalIgnoreCase)) ? "1" : "0" },
                    { "ImprovedOverrun", character.Feats.Any(feat => feat.Name.Equals("Improved Overrun", StringComparison.OrdinalIgnoreCase)) ? "1" : "0" },
                    { "ImprovedSunder", character.Feats.Any(feat => feat.Name.Equals("Improved Sunder", StringComparison.OrdinalIgnoreCase)) ? "1" : "0" },
                    { "ImprovedTrip", character.Feats.Any(feat => feat.Name.Equals("Improved Trip", StringComparison.OrdinalIgnoreCase)) ? "1" : "0" },
                    { "PointBlankShot", character.Feats.Any(feat => feat.Name.Equals("Point-Blank Shot", StringComparison.OrdinalIgnoreCase)) ? "1" : "0" },
                    { "TwoWeaponFighting", character.Feats.Any(feat => feat.Name.Equals("Two-Weapon Fighting", StringComparison.OrdinalIgnoreCase)) ? "1" : "0" },
                    { "ImprovedTwoWeaponFighting", character.Feats.Any(feat => feat.Name.Equals("Improved Two-Weapon Fighting", StringComparison.OrdinalIgnoreCase)) ? "1" : "0" },
                    { "GreaterTwoWeaponFighting", character.Feats.Any(feat => feat.Name.Equals("Greater Two-Weapon Fighting", StringComparison.OrdinalIgnoreCase)) ? "1" : "0" },
                    { "MultiAttack", character.Feats.Any(feat => feat.Name.Equals("Multiattack", StringComparison.OrdinalIgnoreCase)) ? "1" : "0" },
                    { "WeaponFinesse", character.Feats.Any(feat => feat.Name.Equals("Weapon Finesse", StringComparison.OrdinalIgnoreCase)) ? "1" : "0" },
                    { "UncannyDodge", "0" },
                    { "ImprovedTurning", "0" },
                    { "DieHard", character.Feats.Any(feat => feat.Name.Equals("Diehard", StringComparison.OrdinalIgnoreCase)) ? "1" : "0" },
                    { "ImprovedChanneling", character.Feats.Any(feat => feat.Name.Equals("Improved Channel", StringComparison.OrdinalIgnoreCase)) ? "1" : "0" }};

                int attackNumber = 0;
                foreach (Attack attack in character.Attacks)
                {
                    if (attackNumber > 9) break;

                    string damage = string.Format("{0}d{1}{2}{3}", attack.BaseDamage.DiceAmount, attack.BaseDamage.DiceFace, attack.BaseDamage.FlatAmount >= 0 ? "+" : "", attack.BaseDamage.FlatAmount);
                    string damageExtra = "0d1";
                    string damageExtraName = string.Empty;
                    string damageExtraCrit = "0d1";
                    if (attack.ExtraDamages.Count > 0)
                    {
                        Damage extraDamage = attack.ExtraDamages.FirstOrDefault(checkDamage => checkDamage.DiceAmount > 0 || checkDamage.FlatAmount > 0);
                        if (extraDamage != null)
                        {
                            damageExtra = string.Format("{0}d{1}{2}{3}", extraDamage.DiceAmount, extraDamage.DiceFace, extraDamage.FlatAmount > 0 ? "+" : string.Empty, extraDamage.FlatAmount);
                            damageExtraName = extraDamage.DamageName;
                        }
                        Damage critDamage = attack.ExtraDamages.FirstOrDefault(checkDamage => checkDamage.AppliedOnlyOnCrit);
                        if (critDamage != null)
                        {
                            damageExtraCrit = string.Format("{0}d{1}{2}{3}", critDamage.DiceAmount, critDamage.DiceFace, critDamage.FlatAmount > 0 ? "+" : string.Empty, critDamage.FlatAmount);
                        }
                    }
                    Dictionary<string, string> attackDictionary = new Dictionary<string, string>() {
                        { "Name", attack.Name },
                        { "Primary", attack.IsSecondary ? "0" : "1" },
                        { "Quantity", attack.NumberAttacks.ToString() },
                        { "Manufactured", attack.IsNatural ? "0" : "1" },
                        { "AtkBonus", attack.HitBonus.ToString() },
                        { "CritMult", attack.CritMultiplier.ToString() },
                        { "CritRange", attack.CritRange.ToString() },
                        { "Damage", string.Format("{0}d{1}{2}{3}", attack.BaseDamage.DiceAmount, attack.BaseDamage.DiceFace, attack.BaseDamage.FlatAmount >= 0 ? "+" : "", attack.BaseDamage.FlatAmount) },
                        { "DmgMax", attack.MaxStrength < 0 ? "50" : attack.MaxStrength.ToString() },
                        { "DmgExtra", damageExtra },
                        { "DmgExtraName", damageExtraName },
                        { "DmgExtraCrit", damageExtraCrit },
                        { "DmgBonusCap", attack.MaxStrength < 0 ? "50" : attack.MaxStrength.ToString() },
                        { "TwoHanded", attack.IsTwoWeaponFighting ? "1" : "0" },
                        { "Finesse", attack.IsFinessable ? "1" : "0" },
                        { "OHLight", attack.IsTwoWeaponFighting ? (attack.IsOffhandLight ? "1" : "0") : "2" },
                        { "Ranged", attack.IsRanged ? "1" : "0" }};
                    maptoolsProperties.Add(new MaptoolsProperty(string.Format("Weapon{0}", attackNumber), CreateListObject(attackDictionary)));

                    attackNumber++;
                }

                List<State> maptoolsStates = new List<State>();
                maptoolsStates.Add(new State() { Name = "Enraged", Value = isRaging.ToString(), Type = "boolean" });

                string sizeMap = string.Empty;
                switch (character.Size)
                {
                    case SizeType.Colossal: sizeMap = "fwABAeFlFSoJAAAAKgABAQ=="; break;
                    case SizeType.Diminuative: sizeMap = "fwABAc1lFSoCAAAAKgABAQ=="; break;
                    case SizeType.Fine: sizeMap = "fwABAc1lFSoBAAAAKgABAQ=="; break;
                    case SizeType.Gargantuan: sizeMap = "fwABAdFlFSoIAAAAKgABAQ=="; break;
                    case SizeType.Huge: sizeMap = "fwABAdBlFSoHAAAAKgABAA=="; break;
                    case SizeType.Large: sizeMap = "fwABAdBlFSoGAAAAKgABAA=="; break;
                    case SizeType.Medium: sizeMap = "fwABAc9lFSoFAAAAKgABAQ=="; break;
                    case SizeType.Small: sizeMap = "fwABAc5lFSoEAAAAKgABAA=="; break;
                    case SizeType.Tiny: sizeMap = "fwABAc5lFSoDAAAAKgABAA=="; break;
                }

                Token token = new Token()
                {
                    GMName = gmLabel,
                    GMNotes = string.Format("Imported by Bridge.Pathfinder.Maptools.Exporter. Version: {0}\n{1}", Assembly.GetExecutingAssembly().GetName().Version.ToString(), character.Statblock),
                    HasSight = !string.IsNullOrEmpty(sightType),
                    Label = label,
                    Name = character.Name,
                    PortraitImage = new FileInfo(portraitImageLocation),
                    PropertyType = "Pathfinder",
                    SightType = sightType,
                    TokenImage = new FileInfo(tokenImageLocation),
                    TokenType = TokenType.PC,
                    Properties = maptoolsProperties,
                    States = maptoolsStates,
                    SizeMapGuid = sizeMap
                };
                returnValue = token.Output(fileInfo);
            }
            finally
            {
                if (!string.IsNullOrEmpty(tempPortraitDefault) && File.Exists(tempPortraitDefault))
                {
                    try
                    {
                        File.Delete(tempPortraitDefault);
                    }
                    catch (Exception) { }
                }
                if (!string.IsNullOrEmpty(tempTokenDefault) && File.Exists(tempTokenDefault))
                {
                    try
                    {
                        File.Delete(tempTokenDefault);
                    }
                    catch (Exception) { }
                }
            }

            return returnValue;
        }

        public override string GetConverterName()
        {
            return "Pathfinder Maptools Exporter";
        }

        public override string GetConverterDescription()
        {
            return "Pathfinder Maptools Exporter\n" +
                "Converts a Pathfinder Character into a Maptools Token.\n" +
                " by Zachare Sylvestre";
        }

        public override List<ConverterProperty> GetConverterProperties()
        {
            List<ConverterProperty> properties = new List<ConverterProperty>();

            properties.Add(new ConverterProperty()
            {
                Key = "Portrait Image",
                PropertyType = PropertyType.File,
                Value = string.Empty,
                Validator = new Func<object, bool>(theObject => theObject == null || theObject.ToString().Length == 0 || File.Exists(theObject.ToString())),
                FilterExtensions = "Images (*.gif;*.jpg;*.png;*.jpeg)|*.gif;*.jpg;*.png;*.jpeg|All Files (*.*)|*.*"
            });
            properties.Add(new ConverterProperty()
            {
                Key = "Token Image",
                PropertyType = PropertyType.File,
                Value = string.Empty,
                Validator = new Func<object, bool>(theObject => theObject == null || theObject.ToString().Length == 0 || File.Exists(theObject.ToString())),
                FilterExtensions = "Images (*.gif;*.jpg;*.png;*.jpeg)|*.gif;*.jpg;*.png;*.jpeg|All Files (*.*)|*.*"
            });
            properties.Add(new ConverterProperty()
            {
                Key = "Label",
                PropertyType = PropertyType.String,
                Value = string.Empty
            });
            properties.Add(new ConverterProperty()
            {
                Key = "GM Label",
                PropertyType = PropertyType.String,
                Value = string.Empty
            });


            return properties;
        }

        public override string GetOutputExtensions()
        {
            return "Maptools Token (*.rptok)|*.rptok|Zip File (*.zip)|*.zip";
        }

        public override string GetDefaultExtension()
        {
            return "rptok";
        }

        private string GetDisplayName(object theEnum)
        {
            UIDisplayAttribute displayAttribute = ((Enum)theEnum).GetUIDisplayAttribute();
            return displayAttribute != null ? displayAttribute.DisplayName : theEnum.ToString();
        }

        private string GetConversionName(object theEnum)
        {
            UIDisplayAttribute displayAttribute = ((Enum)theEnum).GetUIDisplayAttribute();
            return displayAttribute != null ? displayAttribute.ConversionName : theEnum.ToString();
        }

        private string CreateJSONObject(IDictionary<string, object> dictionary)
        {
            string jsonResult = string.Empty;
            foreach (KeyValuePair<string, object> singleValue in dictionary)
            {
                if (jsonResult.Length > 0) jsonResult += ",";

                int valueInt = -1;
                if (singleValue.Value is int)
                {
                    valueInt = (int)singleValue.Value;
                    jsonResult += string.Format("\"{0}\":{1}", singleValue.Key, valueInt);
                }
                else if (int.TryParse(singleValue.Value.ToString(), out valueInt))
                {
                    jsonResult += string.Format("\"{0}\":{1}", singleValue.Key, valueInt);
                }
                else if (singleValue.Value.ToString().StartsWith("{") || singleValue.Value.ToString().StartsWith("["))
                {
                    jsonResult += string.Format("\"{0}\":{1}", singleValue.Key, singleValue.Value);
                }
                else
                {
                    jsonResult += string.Format("\"{0}\":\"{1}\"", singleValue.Key, singleValue.Value);
                }
            }
            return "{" + jsonResult + "}";
        }

        private string CreateListObject(IDictionary<string, string> dictionary)
        {
            string listResult = string.Empty;
            foreach (KeyValuePair<string, string> singleValue in dictionary)
            {
                listResult += string.Format("{0}={1}; ", singleValue.Key, singleValue.Value);
            }
            return listResult;
        }
    }
}
